# User Guide To Wrapyfi your code: ``` from wrapyfi.connect.wrapper import MiddlewareCommunicator class TheClass(MiddlewareCommunicator) ... @MiddlewareCommunicator.register(...) @MiddlewareCommunicator.register(...) def encapsulated_method(...): ... return encapsulated_a, encapsulated_b def encapsulating_method(...) ... encapsulated_a, encapsulated_b = self.encapsulated_method(...) ... return result, the_class = TheClass() the_class.activate_communication(the_class.encapsulated_method, mode="publish") while True: the_class.encapsulating_method(...) ``` The primary component for facilitating communication is the `MiddlewareCommunicator`. To register the methods for a given class, the class should inherit the `MiddlewareCommunicator`. Any method decorated with `@MiddlewareCommunicator.register(, , , )` is automatically registered by Wrapyfi. The `` is the publisher/listener type for a given method's return. The supported data types are listed [here](). The `` defines the communication medium e.g.: `yarp`, `ros2`, `ros`, or `zeromq`. The default communicator is `zeromq` but can be replaced by setting the environment variables `WRAPYFI_DEFAULT_COMMUNICATOR` or `WRAPYFI_DEFAULT_MWARE` (`WRAPYFI_DEFAULT_MWARE` overrides `WRAPYFI_DEFAULT_COMMUNICATOR` when both are provided) to the middleware of choice e.g.: ``` export WRAPYFI_DEFAULT_COMMUNICATOR=yarp ``` The `` serves no purpose in the current Wrapyfi version, but has been left for future support of module-level decoration, where the methods don't belong to a class, and must therefore have a unique identifier for declaration in the [configuration files](). The `` is the name used for the connected topic and is dependent on the middleware platform. The listener and publisher receive the same topic name. The `@MiddlewareCommunicator.register` decorator is defined for each of the method's returns in the same order. As shown in the example above, the first decorator defines the properties of `encapsulated_a`'s publisher and listener, whereas the second decorator belongs to `encapsulated_b`. A decorated method must always return a tuple which can easily be enforced by adding a `comma` after the return in case a single variable is returned. Lists are also supported for single returns e.g.: ``` @MiddlewareCommunicator.register([..., {...}], [..., {...}], [...]) @MiddlewareCommunicator.register(...) def encapsulated_method(...): ... encapsulated_a = [[...], [...], [...]] ... return encapsulated_a, encapsulated_b ``` ```{warning} Methods with a single return should be followed by a comma e.g., return encapsulated a, . This explicitly casts the return as a tuple to avoid confusion with list returns as single return element/s ``` Each of the list's returns is encapsulated with its own publisher and listener, with the named arguments transmitted as a single dictionary within the list. Notice that `encapsulated_a` returns a list of length 3, therefore, the first decorator contains 3 list configurations as well. This is useful especially when transmitting multiple images or audio chunks over YARP, ROS, and ROS 2. Note that by using a single `NativeObject` as a ``, the same can be achieved. However, the implementation of the `NativeObject` for most middleware serializes the objects as strings before transmission. The `NativeObject` may result in a greater overhead and should only be used when multiple nesting depths are required or the objects within a list are not within the [supported data structure types](#data-structure-types). ### Argument Passing The `$` symbol is used in Wrapyfi to specify that a decorator should update its arguments according to the arguments of the decorated method. This can be useful when the decorator needs to modify its behavior during runtime. For instance: ``` ... @MiddlewareCommunicator.register('NativeObject', '$0', 'ExampleCls', '/example/example_arg_pass', carrier='tcp', should_wait='$blocking') def example_arg_pass(self, mware, msg='', blocking=True): ``` Setting the decorator's keyword argument `should_wait='$blocking'` expects the decorated method to receive a boolean `blocking` argument, altering the encapsulating decorator's behavior when the encapsulated method is called. Setting the decorator's second argument to `$0` acquires the value of `mware` (the first argument passed to `example_arg_pass`) and sets it as the middleware for that method. These arguments take effect on the first invocation of a method. Changing arguments after the first invocation results in no change in behavior, unless a `MiddlewareCommunicator` inheriting class for a given method is [closed](#closing-and-deleting-classes). ### Closing and Deleting Classes Currently, closing a connection requires closing all connections established by every method within that class. ```{warning} Selectively deactivating method connections is not supported [![planned](https://custom-icon-badges.demolab.com/badge/planned%20for%20Wrapyfi%20v1.0-%23C2E0C6.svg?logo=hourglass&logoColor=white)](https://github.com/modular-ml/wrapyfi/issues/99 "planned link") ``` To close and delete a `MiddlewareCommunicator` inheriting class means that the middleware connection will be disconnected gracefully. The class references will be removed from all registries, the communication ports will be freed, and the instance will be destroyed. To close a class instance: ``` # assuming an existing instance-> example_instance = ExampleCls() example_instance.close() del example_instance ```